/**
 * 
 */
package denaris.core;

import java.util.Vector;

import javax.microedition.lcdui.game.Sprite;

import denaris.core.controller.AbstractController;
import denaris.core.entity.AbstractEntity;
import denaris.midlet.GameScreen;

/**
 * @author stkl23
 *
 */
public class MissileEmitter extends BulletEmitter{
	public static final int MISSILE_SPEED = 12;
	
	public class MissileController extends AbstractController{
		private AbstractDenarisMap map;
		private int localDeltaX;
		private int localDeltaY;
		private int frameDelay;
		
		public MissileController(AbstractDenarisMap map) {
			super();
			setMap(map);
			reset();
		}
		
		public void reset() {
			super.reset();
			setLocalDeltas(getDeltaX(), getDeltaY());
			if (controllable != null) {
				controllable.setFrame(localDeltaY < 0 ? 1 : 3);
			}
			frameDelay = 3;
		}
		
		/**
		 * Returns the nearest opponent
		 */
		private AbstractEntity findNextOpponent() {
			AbstractEntity result = null;
			double distance = 1000;
			Vector enemies = getMap().getEnemies();
			for (int i = 0; i<enemies.size(); i++) {
				AbstractEntity entity = (AbstractEntity) enemies.elementAt(i);
				double curDistance = entity.getDistance(controllable.getX(), controllable.getY());
				if (curDistance < distance) {
					distance = curDistance;
					result = entity;
				}
			}
			return result;
		}
		
		public boolean advance() {
			if (frameDelay > 0) {
				frameDelay--;
			}else {
				AbstractEntity opponent = findNextOpponent();
				if (opponent != null) {
					Sprite sprite = opponent.getController().getSprite();
					// set the new direction
					int x = sprite.getX();
					int y = sprite.getY();
					int dx = x - controllable.getX();
					int dy = y - controllable.getY();
					int absDx = Math.abs(dx);
					int absDy = Math.abs(dy);
					if (Math.abs(Math.floor(Math.sqrt(absDy)) - Math.floor(Math.sqrt(absDx))) < 2) {
						// move diagonal
						if (dx > 0 && dy > 0) {
							controllable.setFrame(3);
							setLocalDeltas(MISSILE_SPEED, MISSILE_SPEED);
						}else if (dx > 0 && dy < 0) {
							controllable.setFrame(1);
							setLocalDeltas(MISSILE_SPEED, -MISSILE_SPEED);
						}else if (dx < 0 && dy > 0) {
							controllable.setFrame(5);
							setLocalDeltas(-MISSILE_SPEED, MISSILE_SPEED);
						}else if (dx < 0 && dy < 0) {
							controllable.setFrame(7);
							setLocalDeltas(-MISSILE_SPEED, -MISSILE_SPEED);
						}
					}else {
						// move horizontal or vertical
						if (absDx > absDy) {
							controllable.setFrame(dx < 0 ? 6 : 2);
							setLocalDeltas(dx < 0 ? -MISSILE_SPEED : MISSILE_SPEED, 0);
						}else {
							controllable.setFrame(dy < 0 ? 0 : 4);
							setLocalDeltas(0, dy < 0 ? -MISSILE_SPEED : MISSILE_SPEED);
						}
					}
				}
				// reset the frame delay and keep moving in the current direction
				frameDelay = GameScreen.nextPositiveInt(3);
			}
			controllable.move(localDeltaX, localDeltaY);
			return isInScope(controllable.getX(), controllable.getY());
		}

		public AbstractDenarisMap getMap() {
			return map;
		}

		public void setMap(AbstractDenarisMap map) {
			this.map = map;
		}

		public void setLocalDeltas(int dx, int dy) {
			this.localDeltaX = dx;
			this.localDeltaY = dy;
		}

		public void setFrameDelay(int frameDelay) {
			this.frameDelay = frameDelay;
		}
	}
	
	private AbstractDenarisMap map;
	
	public MissileEmitter(int cacheSize, int dx, int dy, AbstractDenarisMap map) {
		super(cacheSize, dx, dy, "/denaris/resource/sprite/missile1.png", 8, 8);
		setMap(map);
	}

	public boolean collidesWith(AbstractParticle particle, AbstractEntity entity) {
		return particle.getController().getSprite().collidesWith(entity.getController().getSprite(), false);
	}

	public AbstractController createController() {
		return new MissileController(getMap());
	}

	protected void initializeEmittedParticle(int x, int y, AbstractParticle particle) {
		// set the normal stuff...
		super.initializeEmittedParticle(x, y, particle);
		MissileController ctrl = (MissileController) particle.getController();
		ctrl.reset();
	}

	public AbstractDenarisMap getMap() {
		return map;
	}

	public void setMap(AbstractDenarisMap map) {
		this.map = map;
	}

	public boolean collidesWith(AbstractParticle particle, TiledMap map) {
		return particle.getController().getSprite().collidesWith(map.getLayer(), false);
	}
}